package com.neu.gulimall.search.thread;

import java.util.concurrent.*;

public class ThreadTest {

    public static ExecutorService service = Executors.newFixedThreadPool(10);

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        System.out.println("main-----start-----");
        /*
            java实现多线程的4种方法
            1、继承Thread类
                     Thread01 thread = new Thread01();
                     thread.setName("Thread001");
                     thread.start(); // 启动一个新的线程,然后该线程去执行run方法
                     //thread.run(); // 并不启动线程，只是在当前线程中执行run方法

            2、实现Runnable接口
                    Runnable01 runnable01 = new Runnable01();
                    Thread thread2 = new Thread(runnable01);
                    thread2.setName("Thread002");
                    thread2.start();

            3、实现Callable接口 + FutureTask （可以拿到返回结果，可以处理异常）
                    Callable01 callable01 = new Callable01();
                    FutureTask futureTask = new FutureTask(callable01);
                    Thread thread3 = new Thread(futureTask);
                    thread3.setName("Thread003");
                    thread3.start();

                    // 阻塞等待，会一直等待直到整个线程执行完成，获取返回结果
                    //Integer i = (Integer) futureTask.get();

            4、线程池----给线程池直接提交任务    service.execute(new Runnable01());
                1、创建
                    1）使用 Executors 工具类

            我们以后在业务代码里面，前三种启动线程的方式都不用，采取线程池的方式。【将所有的多线程异步任务都交给线程池来执行】
            线程池一般在系统中都只有一个。
                开发中为什么要使用线程池：
                1、降低资源的消耗
                    通过重复利用已经创建好的线程降低线程的创建和销毁带来的损耗
                2、提高响应速度
                    因为线程池中的线程数没有超过线程池的最大上限时，有的线程处于等待分配任务的状态，当任务来时无需创建新的线程就能执行
                3、提高线程的可管理性
                    线程池会根据当前的系统特点对池内的线程进行优化处理，减少创建和销毁线程带来的系统开销。无限的创建和销毁线程不仅消耗资源，还降低系统的稳定性，
                    使用线程池进行统一分配，可以提高线程的可管理性

            区别：
                1、2不能得到返回值，3可以获取返回值
                1、2、3都不能控制资源
                4可以控制资源，性能稳定
         */

        /* 原生创建线程池的七大参数
          int corePoolSize, 核心线程数【一直存在（即使是空闲的），除非设置了 allowCoreThreadTimeOut】；线程池创建好后就准备就绪的线程数量，就等待来接收异步任务去执行。
          int maximumPoolSize,最大线程数量，用以控制资源
          long keepAliveTime,存活时间，如果当前的线程数量大于核心线程的数量，会释放非核心的空闲线程。即只要非核心线程的空闲时间大于了设置的keepAliveTime，就会被线程池回收
          TimeUnit unit, 存活时间的时间单位
          BlockingQueue<Runnable> workQueue,阻塞队列，如果需要执行的任务有很多，就会将目前多余的任务放到队列中，只要有线程空闲了，就去队列中取出新的任务继续执行
          ThreadFactory threadFactory,线程的创建工厂
          RejectedExecutionHandler handler,如果队列满了，按照我们指定的拒绝策略来拒绝执行任务

          运行流程：
                1、线程池创建，准备好 core 数量的核心线程，用来准备接收任务
                2、新的任务进来，用 core 准备好的空闲核心线程去执行任务
                    2.1 core满了，就将再进来的任务放入阻塞队列中，空闲的 core 就会自己去阻塞队列中获取任务执行
                    2.2 当阻塞队列满了，就直接开新线程执行，最大只能开到 max 指定的数量
                    2.3 如果线程数开到了max的数量，还有新任务进来，就会使用设置的拒绝策略进行处理
                    2.4 max个线程将任务执行完之后，非核心的空闲线程在指定的存活时间过后自动销毁，线程池中的线程数量最终保持为核心线程数量

                3、所有的线程的创建都是由指定的factory创建的

                new LinkedBlockingQueue<>(),默认的阻塞队列的大小是Integer的最大值，会导致内存不够，所以在创建的时候要记得设置好合适的大小
         */
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,
                200,
                10,
                TimeUnit.SECONDS,
                new LinkedBlockingQueue<>(100000),
                Executors.defaultThreadFactory(),
                // 采取丢弃的拒绝策略，直接将新来的任务丢掉
                new ThreadPoolExecutor.AbortPolicy());
        // Executors.newCachedThreadPool(); 核心线程数是0，但最大可以有 Integer.MAX_VALUE 这么多个线程，所有的线程都可以回收
        //Executors.newFixedThreadPool(); 固定大小，核心线程数 == 最大线程数  所有线程均不可回收
        //Executors.newScheduledThreadPool(); 定时任务的线程池
        //Executors.newSingleThreadExecutor(); 单线程的线程池，核心线程数 == 最大线程数 == 1 ，后台从队列中获取任务，挨个执行

        System.out.println("main-----end-----" );

    }

    public static class Thread01 extends Thread{

        @Override
        public void run() {  // 重写run方法
            System.out.println("Thread001------start-----");
            System.out.println("当前线程----->" + Thread.currentThread().getName());
            int i = 10;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("运行结果---->" + i/2);
            System.out.println("Thread001------end-----");
        }
    }

    public static class Runnable01 implements Runnable{

        @Override
        public void run() {
            System.out.println("Thread002------start-----");
            System.out.println("当前线程----->" + Thread.currentThread().getName());
            int i = 10;
            System.out.println("运行结果---->" + i/2);
            System.out.println("Thread002------end-----");
        }
    }

    public static class Callable01 implements Callable<Integer>{

        @Override
        public Integer call() throws Exception {
            System.out.println("Thread003------start-----");
            System.out.println("当前线程----->" + Thread.currentThread().getName());
            int i = 10/2;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("运行结果---->" + i);
            System.out.println("Thread003------end-----");
            return i;
        }
    }
}
